local window = window or {};

-- 注册元素类
local __element_class_map__ = {};
function commonlib.GetElementClassMap()
    return __element_class_map__;
end

function commonlib.RegisterElementClass(tagname, cls)
    __element_class_map__[string.lower(tagname)] = cls;
end

function commonlib.GetElementClassByTag(tagname)
    return __element_class_map__[string.lower(tagname or "Element")];
end

commonlib.__window_directory__ = commonlib.FormatPath(commonlib.__script_directory__ .. "/window");
commonlib.__window_filename__ = commonlib.FormatPath(commonlib.__window_directory__ .. "/init.lua");
local Elements = commonlib.import("./Element/init.lua", commonlib.__window_filename__);
local Widgets = commonlib.import("./Widget/init.lua", commonlib.__window_filename__);

window.Elements = Elements;
window.Widgets = Widgets;

commonlib.setfield("Window.Elements", Elements);
commonlib.setfield("Window.Widgets", Widgets);

local Window = commonlib.import("./Window.lua", commonlib.__window_filename__);
local Event = commonlib.import("./Event.lua", commonlib.__window_filename__);
local KeyBoard = commonlib.import("./KeyBoard.lua", commonlib.__window_filename__);
commonlib.setfield("Window.Window", Window);
commonlib.setfield("Window.Event", Event);
commonlib.setfield("Window.KeyBoard", KeyBoard);

commonlib.__windows__ = {};
commonlib.__window_map__ = {};

commonlib.__main_window_width__, commonlib.__main_window_height__ = commonlib.GetMainWindowWidthHeight();
commonlib.__design_window_width__, commonlib.__design_window_height__ = 0, 0 -- 1280, 720;
commonlib.__window_scale__ = commonlib.GetMainWindowScale();
commonlib.__window_width__, commonlib.__window_height__ = commonlib.__main_window_width__, commonlib.__main_window_height__;
commonlib.__mouse_x__, commonlib.__mouse_y__ = 0, 0;

commonlib.GetWindowWidthHeight = function() 
    return commonlib.__window_width__, commonlib.__window_height__;
end
commonlib.GetDesignWindowWidthHeight = function() 
    return commonlib.__design_window_width__, commonlib.__design_window_height__;
end
commonlib.SetDesignWindowWidthHeight = function(designWindowWidth, designWindowHeight)
    commonlib.__design_window_width__, commonlib.__design_window_height__ = designWindowWidth or commonlib.__design_window_width__, designWindowHeight or commonlib.__design_window_height__;
    if (commonlib.__design_window_width__ == 0 or commonlib.__design_window_height__ == 0 or commonlib.__main_window_width__ == 0 or commonlib.__main_window_height__ == 0) then return end
    commonlib.__window_scale__ = math.min(commonlib.__main_window_width__ / commonlib.__design_window_width__, commonlib.__main_window_height__ / commonlib.__design_window_height__);
    commonlib.__window_width__, commonlib.__window_height__ = math.floor(commonlib.__main_window_width__ / commonlib.__window_scale__), math.floor(commonlib.__main_window_height__ / commonlib.__window_scale__);
    commonlib.SetMainWindowScale(commonlib.__window_scale__);
end

local function SetMainWindowWidthHeight(mainWindowWidth, mainWindowHeight)
    commonlib.__main_window_width__, commonlib.__main_window_height__ = mainWindowWidth or commonlib.__main_window_width__, mainWindowHeight or commonlib.__main_window_height__;
    commonlib.__window_width__, commonlib.__window_height__ = commonlib.__main_window_width__, commonlib.__main_window_height__;
    if (commonlib.__design_window_width__ == 0 or commonlib.__design_window_height__ == 0 or commonlib.__main_window_width__ == 0 or commonlib.__main_window_height__ == 0) then return end
    commonlib.__window_scale__ = math.min(commonlib.__main_window_width__ / commonlib.__design_window_width__, commonlib.__main_window_height__ / commonlib.__design_window_height__);
    commonlib.__window_width__, commonlib.__window_height__ = math.floor(commonlib.__main_window_width__ / commonlib.__window_scale__), math.floor(commonlib.__main_window_height__ / commonlib.__window_scale__);
    commonlib.SetMainWindowScale(commonlib.__window_scale__);
end

commonlib.OnWindowCreate = function(target_window)
    -- 添加至窗口列表
    local zorder = target_window:GetZOrder();
    local is_added = false;
    for index, win in ipairs(commonlib.__windows__) do
        if (win == target_window) then 
            is_added = true;
            break; 
        end
        -- zorder 由小到大排列
        if (zorder < win:GetZOrder()) then
            table.insert(commonlib.__windows__, index, target_window);
            is_added = true;
            break;
        end
    end
    if (not is_added) then table.insert(commonlib.__windows__, target_window) end
end

commonlib.OnWindowDestroy = function(target_window)
    -- 从窗口列表移除
    for index, win in ipairs(commonlib.__windows__) do
        if (win == target_window) then
            table.remove(commonlib.__windows__, index);
            break;
        end
    end
end

commonlib.ShowWindow = function(params)
    local win = Window:new():Init(params);
    local key = win:GetKey();

    if (key) then
        local _win = commonlib.__window_map__[key];
        if (_win) then
            _win:Close();
            commonlib.__window_map__[key] = nil;
        end
        commonlib.__window_map__[key] = win;
    end

    win:Show();

    return win;
end

local function GetMouseWindow(mouse_x, mouse_y)
    local mouse_capture_window = Window.GetMouseCaptureWindow();
    if (mouse_capture_window) then return mouse_capture_window end 

    local active_window = Window.GetActiveWindow();
    if (active_window) then 
        local winX, winY, winW, winH = active_window:GetWindowPosition();
        if (winX <= mouse_x and mouse_x <= (winX + winW) and winY <= mouse_y and mouse_y <= (winY + winH)) then
            return active_window;
        end
    end 

    local size = #(commonlib.__windows__);
    for i = size, 1, -1 do
        local win = commonlib.__windows__[i];
        local winX, winY, winW, winH = win:GetWindowPosition();
        if (winX <= mouse_x and mouse_x <= (winX + winW) and winY <= mouse_y and mouse_y <= (winY + winH)) then
            return win;
        end
    end

    return commonlib.__windows__[size];
end

local function GetActiveWindow()
    local active_window = Window.GetActiveWindow();
    if (active_window) then return active_window end 
    return commonlib.__windows__[#(commonlib.__windows__)];
end

local function OnPaint()
    -- print("__on_paint__");
    for _, win in ipairs(commonlib.__windows__) do
        if (win:IsShow()) then
            win:OnPaint();
        end
    end
end

local function OnSize(width, height)
    -- print("__on_size__ width = " .. width .. ", height = " .. height);
    SetMainWindowWidthHeight(width, height);
    for _, win in ipairs(commonlib.__windows__) do
        win:OnSize(commonlib.__window_width__, commonlib.__window_height__);
    end
end

local function OnMouseDown(x, y, mouse_button)
    -- print("__on_mouse_down__", x, y, mouse_button);
    commonlib.__mouse_x__, commonlib.__mouse_y__ = x, y;
    local win = GetMouseWindow(x, y);
    local event = Event:InitMouseEvent(win, "onmousedown", x, y, mouse_button);
    return win and win:OnEvent(event);
end

local function OnMouseMove(x, y)
    commonlib.__mouse_x__, commonlib.__mouse_y__ = x, y;
    -- print("__on_mouse_move__", x, y);
    local win = GetMouseWindow(x, y);
    local event = Event:InitMouseEvent(win, "onmousemove", x, y);
    return win and win:OnEvent(event);
end

local function OnMouseUp(x, y, mouse_button)
    commonlib.__mouse_x__, commonlib.__mouse_y__ = x, y;
    -- print("__on_mouse_up__", x, y, mouse_button);
    local win = GetMouseWindow(x, y);
    local event = Event:InitMouseEvent(win, "onmouseup", x, y, mouse_button);
    return win and win:OnEvent(event);
end

local function OnMouseWheel(mouse_wheel_x, mouse_wheel_y)
    local win = GetMouseWindow(x, y);
    local event = Event:InitMouseEvent(win, "onmousewheel", nil, nil, 0, mouse_wheel_x, mouse_wheel_y);
    return win and win:OnEvent(event);
end

local function OnKeyDown(keycode)
    -- print("__on_key_down__", keycode);
    KeyBoard:SetKeyPressed(keycode, true);
    local win = GetActiveWindow();
    local event = Event:InitKeyEvent(win, "onkeydown", keycode);
    return win and win:OnEvent(event);
end

local function OnKeyUp(keycode)
    -- print("__on_key_up__", keycode);
    KeyBoard:SetKeyPressed(keycode, false);
    local win = GetActiveWindow();
    local event = Event:InitKeyEvent(win, "onkeyup", keycode);
    return win and win:OnEvent(event);
end

local function OnChar(key)
    -- print("__on_char__", key);
    local win = GetActiveWindow();
    local event = Event:InitCharEvent(win, key);
    return win and win:OnEvent(event);
end

-- C++ CallBack
_G.__on_paint__ = commonlib.__xpcall_callback__(OnPaint);

-- C++ CallBack
_G.__on_size__ = commonlib.__xpcall_callback__(OnSize);

-- C++ CallBack
_G.__on_mouse_down__ = commonlib.__xpcall_callback__(OnMouseDown);

-- C++ CallBack
_G.__on_mouse_move__ = commonlib.__xpcall_callback__(OnMouseMove);

-- C++ CallBack
_G.__on_mouse_up__ = commonlib.__xpcall_callback__(OnMouseUp);

-- C++ CallBack
_G.__on_mouse_wheel__ = commonlib.__xpcall_callback__(OnMouseWheel);

-- C++ CallBack
_G.__on_key_down__ = commonlib.__xpcall_callback__(OnKeyDown);

-- C++ CallBack
_G.__on_key_up__ = commonlib.__xpcall_callback__(OnKeyUp);

-- C++ CallBack
_G.__on_char__ = commonlib.__xpcall_callback__(OnChar);

return window;
